/* 
 * File:   flacDecode.cpp
 * Author: hao
 * 
 * Created on 2011年5月10日, 下午5:46
 */

#include "flacDecode.h"
#include <cstdlib>
#include <cstdio>
#include <cstring>
using namespace std;

flacDecode::flacDecode() {

    overflowBuffer = NULL;

    overflowBufferLength = 0;

    frameDataBuffer = NULL;
    frameDataBufferLength = 0;

    pdecoder = FLAC__stream_decoder_new();

}

bool flacDecode::check(void * pData, unsigned long long length) {

    if (length <= 4) return false;

    char * pcheck = (char*) pData;
    //SKIP ID3 Tag.......
    if (pcheck[0] == 'I' && pcheck[1] == 'D' && pcheck[2] == '3') {
        unsigned off = 0;
        //ID3v2 size: 4 * 0xxxxxxx
        off |= ((pcheck[ 6 ] & 255) << 21) | ((pcheck[7] & 255) << 14) | ((pcheck[8] & 255) << 7) | ( pcheck[9] & 255 );

        pcheck = pcheck + 10 + off;
    }
    if (pcheck[0] != 'f' || pcheck[1] != 'L' || pcheck[2] != 'a' || pcheck[3] != 'C') {

        return false;

    }

    FLAC__stream_decoder_finish(pdecoder);
    if (FLAC__STREAM_DECODER_INIT_STATUS_OK != FLAC__stream_decoder_init_stream(pdecoder, ReadCallback, SeekCallback, TellCallback, LengthCallback, EofCallback, WriteCallback, MetaCallback, ErrorCallback, this))
        return false;

    Length = length;

    data = (char *) pData;

    currentPos = 0;

    delete [] frameDataBuffer;

    delete [] overflowBuffer;

    frameDataBuffer = NULL;
    overflowBuffer = NULL;
    frameDataBufferLength = 0;
    overflowBufferLength = 0;

    return true;

}

FLAC__StreamDecoderReadStatus flacDecode::ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) {

    flacDecode * pself = (flacDecode *) client_data;

    if (*bytes == 0 || pself->currentPos >= pself->Length)
        return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;


    if (pself->currentPos + *bytes >= pself->Length) {
        *bytes = pself->Length - pself->currentPos;
    }

    memcpy(buffer, pself->data + pself->currentPos, *bytes);
    pself->currentPos += *bytes;


    return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

FLAC__StreamDecoderSeekStatus flacDecode::SeekCallback(const FLAC__StreamDecoder* decoder, FLAC__uint64 absolute_byte_offset, void* client_data) {

    flacDecode * pself = (flacDecode *) client_data;

    if (absolute_byte_offset >= pself->Length)
        return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;

    pself->currentPos = absolute_byte_offset;

    return FLAC__STREAM_DECODER_SEEK_STATUS_OK;

}

FLAC__StreamDecoderTellStatus flacDecode::TellCallback(const FLAC__StreamDecoder* decoder, FLAC__uint64* absolute_byte_offset, void* client_data) {
    flacDecode * pself = (flacDecode *) client_data;

    *absolute_byte_offset = pself->currentPos;

    return FLAC__STREAM_DECODER_TELL_STATUS_OK;

}

FLAC__StreamDecoderLengthStatus flacDecode::LengthCallback(const FLAC__StreamDecoder* decoder, FLAC__uint64* stream_length, void* client_data) {
    flacDecode * pself = (flacDecode *) client_data;

    *stream_length = pself->Length;

    return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;

}

FLAC__bool flacDecode::EofCallback(const FLAC__StreamDecoder* decoder, void* client_data) {
    flacDecode * pself = (flacDecode *) client_data;

    return pself->Length == pself->currentPos;
}

void flacDecode::ErrorCallback(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErrorStatus status, void* client_data) {

}

FLAC__StreamDecoderWriteStatus flacDecode::WriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) {
    flacDecode * pself = (flacDecode *) client_data;

    delete [] pself->frameDataBuffer;
    pself->frameDataBuffer = new char[ frame->header.channels * frame->header.blocksize * frame->header.bits_per_sample / 8 ];
    pself->frameDataBufferLength = frame->header.channels * frame->header.blocksize * frame->header.bits_per_sample / 8;

    unsigned samplecount = 0;

    unsigned channel = frame->header.channels;
    unsigned samplebytes = frame->header.bits_per_sample / 8;

    unsigned updatebytes = samplebytes * channel;
    FLAC__int32 tmp;
    //好吧，风骚的合并>_<
    for (; samplecount < frame->header.blocksize; samplecount++) {

        for (unsigned i = 0; i < channel; i++) {
            tmp = buffer[i][ samplecount ];
            //tmp >>= 8 * ( 4 - samplebytes );
            for (unsigned k = 0; k < samplebytes; k++) {
                pself->frameDataBuffer[ samplecount * updatebytes + i * samplebytes + k ] = tmp & 511;
                tmp >>= 8;
            }
        }

    }

    pself->currentSample += frame->header.blocksize;

    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;

}

void flacDecode::MetaCallback(const FLAC__StreamDecoder* decoder, const FLAC__StreamMetadata* metadata, void* client_data) {
    flacDecode * pself = (flacDecode *) client_data;

    if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
        pself->nchannel = metadata->data.stream_info.channels;
        pself->nformat = metadata->data.stream_info.bits_per_sample;
        pself->nrate = metadata->data.stream_info.sample_rate;
        pself->ntotalsample = metadata->data.stream_info.total_samples;
    }

}

bool flacDecode::Decoding(void* pTarget, size_t length, size_t* ReadSize) {

    if (overflowBufferLength) {
        if (overflowBufferLength <= length) {
            memcpy((char *) pTarget, overflowBuffer, overflowBufferLength);
            *ReadSize = overflowBufferLength;
            delete [] overflowBuffer;
            overflowBufferLength = 0;
            overflowBuffer = NULL;
        } else {
            memcpy((char *) pTarget, overflowBuffer, length);
            char * tmp = new char[ overflowBufferLength - length ];
            memcpy(tmp, overflowBuffer + length, overflowBufferLength - length);
            delete [] overflowBuffer;
            overflowBufferLength = overflowBufferLength - length;
            overflowBuffer = tmp;
            *ReadSize = length;
            return true;
        }
    }

    if (currentSample > endSample) return false;

    //skip to startFrame;
    while (currentSample < startSample) {
        FLAC__stream_decoder_seek_absolute(pdecoder, startSample);
        currentSample = startSample;
    }

    while (currentSample <= endSample && *ReadSize < length) {
        if (!FLAC__stream_decoder_process_single(pdecoder)) return false;
        if( !frameDataBuffer ) return false;

        if (frameDataBufferLength <= length - *ReadSize) {
            memcpy((char*) pTarget + *ReadSize, frameDataBuffer, frameDataBufferLength);

            *ReadSize += frameDataBufferLength;
        } else {

            memcpy((char*) pTarget + *ReadSize, frameDataBuffer, length - *ReadSize);
            delete [] overflowBuffer;
            overflowBuffer = new char[ frameDataBufferLength - (length - *ReadSize) ];
            overflowBufferLength = frameDataBufferLength - (length - *ReadSize);

            memcpy(overflowBuffer, frameDataBuffer + (length - *ReadSize), overflowBufferLength);

            *ReadSize = length;

        }

        delete [] frameDataBuffer;
        frameDataBuffer = NULL;

    }

    return true;
}

bool flacDecode::SetTrack(int Min, int Second, int Frame, int EndMin, int EndSecond, int EndFrame) {

    startSample = ((unsigned long long) Min * 60 + (unsigned long long) Second + (unsigned long long) Frame / 75) * (unsigned long long) nrate;

    if (EndMin == -1 || EndSecond == -1 || EndFrame == -1) {
        getTotalTime(&EndMin, &EndSecond, &EndFrame);
    }

    endSample = ((unsigned long long) EndMin * 60 + (unsigned long long) EndSecond + (unsigned long long) EndFrame / 75) * (unsigned long long) nrate;

    currentSample = 0;

    return true;

}

bool flacDecode::Seek(int Min, int Second, int Frame) {

    long long seekSample = (Min * 60 + Second + Frame / 75) * nrate;

    FLAC__stream_decoder_seek_absolute(pdecoder, currentSample + seekSample);

    return true;

}

void flacDecode::getTotalTime(int* pMin, int* pSecond, int* pFrame) {

    double sec = (double) ntotalsample / nrate;

    *pMin = (int) sec / 60;
    *pSecond = (int) sec % 60;
    *pFrame = (sec - (int) sec) * 75;


}

void flacDecode::getStatus(unsigned* pFormat, unsigned* pChannels, unsigned* pRate) {

    FLAC__stream_decoder_process_until_end_of_metadata(pdecoder);

    *pChannels = nchannel;
    *pFormat = nformat;
    *pRate = nrate;

}

flacDecode::~flacDecode() {
    
     delete [] frameDataBuffer;
     delete [] overflowBuffer;

    FLAC__stream_decoder_finish(pdecoder);
    FLAC__stream_decoder_delete(pdecoder);

}

